package main;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by liwei.b on 2016/9/12.
 */
public class ObjectHelper {
    public static boolean copyFieldValues(Object from, Object to) {
        boolean flag = false;
        if (from == null || to == null) {
            return flag;
        }
        Class fromClass = from.getClass();
        Class toClass = to.getClass();
        List<Field> fromFields = _getAllFieldsFromObjectClass(fromClass);
        List<Method> fromMethods = _getAllMethodsFromObjectClass(fromClass);
        List<Field> toFields = _getAllFieldsFromObjectClass(toClass);
        List<Method> toMethods = _getAllMethodsFromObjectClass(toClass);

        if ((fromFields == null || fromMethods.isEmpty()) && (toFields == null || toFields.isEmpty()) && (toMethods == null || toMethods.isEmpty())) {
            return flag;
        }
        if (toFields != null && !toFields.isEmpty()) {
            _copySimpleFieldsOnly(from, to, toFields, fromFields, fromMethods, null, false);
        }
        if (toMethods != null && !toMethods.isEmpty()) {
            _copyFieldsWithGeterSeter(from, to, toMethods, fromFields, fromMethods, null, false);
        }
        flag = true;
        return flag;
    }

    public static boolean copyFieldValuesExcludeFields(Object from, Object to, List<String> excludeFields) {
        boolean flag = false;
        if (from == null || to == null) {
            return flag;
        }
        if (excludeFields == null || excludeFields.isEmpty()) {
            return copyFieldValues(from, to);
        }
        Class fromClass = from.getClass();
        Class toClass = to.getClass();
        List<Field> fromFields = _getAllFieldsFromObjectClass(fromClass);
        List<Method> fromMethods = _getAllMethodsFromObjectClass(fromClass);
        List<Field> toFields = _getAllFieldsFromObjectClass(toClass);
        List<Method> toMethods = _getAllMethodsFromObjectClass(toClass);

        if ((fromFields == null || fromMethods.isEmpty()) && (toFields == null || toFields.isEmpty()) && (toMethods == null || toMethods.isEmpty())) {
            return flag;
        }
        if (toFields != null && !toFields.isEmpty()) {
            _copySimpleFieldsOnly(from, to, toFields, fromFields, fromMethods, excludeFields, false);
        }
        if (toMethods != null && !toMethods.isEmpty()) {
            _copyFieldsWithGeterSeter(from, to, toMethods, fromFields, fromMethods, excludeFields, false);
        }
        flag = true;
        return flag;
    }

    public static boolean copyFieldValuesIncludeFields(Object from, Object to, List<String> includeFields) {
        boolean flag = false;
        if (from == null || to == null) {
            return flag;
        }
        Class fromClass = from.getClass();
        Class toClass = to.getClass();
        List<Field> fromFields = _getAllFieldsFromObjectClass(fromClass);
        List<Method> fromMethods = _getAllMethodsFromObjectClass(fromClass);
        List<Field> toFields = _getAllFieldsFromObjectClass(toClass);
        List<Method> toMethods = _getAllMethodsFromObjectClass(toClass);

        if ((fromFields == null || fromMethods.isEmpty()) && (toFields == null || toFields.isEmpty()) && (toMethods == null || toMethods.isEmpty())) {
            return flag;
        }
        if (toFields != null && !toFields.isEmpty()) {
            _copySimpleFieldsOnly(from, to, toFields, fromFields, fromMethods, includeFields, true);
        }
        if (toMethods != null && !toMethods.isEmpty()) {
            _copyFieldsWithGeterSeter(from, to, toMethods, fromFields, fromMethods, includeFields, true);
        }
        flag = true;
        return flag;
    }

    /**
     * 将from对象中同名的字段的值 copy给 to 对象
     *
     * @param from
     * @param to
     * @param toFields       to对象的全部字段集合
     * @param fromFields     from对象的全部字段集合
     * @param fromMethods    from对象的全部方法集合
     * @param fieldsList     需要排除 或者 只需要copy值的字段集合
     * @param isIncludedList true： 只包含fieldsList里面的字段需要copy， false：排除fieldsList中的字段， 其余的需要copy
     */
    private static void _copySimpleFieldsOnly(Object from, Object to, List<Field> toFields, List<Field> fromFields, List<Method> fromMethods, List<String> fieldsList, boolean isIncludedList) {
        for (Field field : toFields) {
            if (_canTheFieldBeCopiedValue(field)) {
                boolean isInTheList = _isTheTheFieldInList(field.getName(), fieldsList);
                if ((isIncludedList && isInTheList) || (!isInTheList && !isIncludedList)) {
                    Object tmpValue = _getValueByFieldName(field.getName(), from, fromFields, fromMethods);
                    if (tmpValue == null) {
                        continue;
                    }
                    try {
                        field.set(to, tmpValue);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
    }

    private static boolean _isTheTheFieldInList(String fieldName, List<String> list) {
        boolean result = false;
        if (list == null || list.isEmpty()) {
            result = false;
            return result;
        }
        if (fieldName == null || fieldName.length() == 0) {
            result = false;
            return result;
        }
        for (String item : list) {
            if (item.equalsIgnoreCase(fieldName)) {
                result = true;
                break;
            }
        }
        return result;
    }

    private static void _copyFieldsWithGeterSeter(Object from, Object to, List<Method> toMethods, List<Field> fromFields, List<Method> fromMethods, List<String> fieldsList, boolean isIncludedList) {
        for (Method method : toMethods) {
            if (_isSetMethodToBeCopiedValue(method)) {
                String strFieldNameOfSetMethod = method.getName().substring(3);
                boolean isInTheList = _isTheTheFieldInList(strFieldNameOfSetMethod, fieldsList);
                if ((isIncludedList && isInTheList) || (!isInTheList && !isIncludedList)) {
                    Object tmpValue = _getValueByFieldName(strFieldNameOfSetMethod, from, fromFields, fromMethods);
                    if (tmpValue == null) {
                        continue;
                    }
                    try {
                        method.invoke(to, tmpValue);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    private static Object _getValueByFieldName(String fieldName, Object from, List<Field> fromFields, List<Method> fromMethods) {
        Object tmpValue = null;
        if (fromFields != null && !fromFields.isEmpty()) {
            for (Field field : fromFields) {
                if (field.getName().equalsIgnoreCase(fieldName)) {
                    try {
                        field.setAccessible(true);
                        tmpValue = field.get(from);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
                if (tmpValue != null) {
                    break;
                }
            }
        }

        if (tmpValue == null && (fromMethods != null && !fromMethods.isEmpty())) {
            for (Method method : fromMethods) {
                if (_isGetMethodToBeCopiedValue(fieldName, method)) {
                    try {
                        tmpValue = method.invoke(from);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
                if (tmpValue != null) {
                    break;
                }
            }
        }
        return tmpValue;
    }

    private static boolean _isSetMethodToBeCopiedValue(Method method) {
        if (method == null) {
            return false;
        }
        String strMethodName = method.getName().toLowerCase();
        if (strMethodName.length() <= 3) {
            return false;
        }
        if (!strMethodName.startsWith("set")) {
            return false;
        }
        return _canCopied(method.getModifiers());
    }

    private static boolean _isGetMethodToBeCopiedValue(String fieldName, Method method) {
        if (method == null) {
            return false;
        }
        String strMethodName = method.getName().toLowerCase();
        if (strMethodName.length() <= 3) {
            return false;
        }
        if (!strMethodName.startsWith("get")) {
            return false;
        }
        if (!strMethodName.substring(3).equalsIgnoreCase(fieldName)) {
            return false;
        }
        return _canCopied(method.getModifiers());
    }

    private static boolean _canTheFieldBeCopiedValue(Field field) {
        if (field == null) {
            return false;
        }
        return _canCopied(field.getModifiers());
    }

    private static boolean _canCopied(int modifier) {
        if (Modifier.isFinal(modifier)) {
            return false;
        }
        if (Modifier.isPrivate(modifier)) {
            return false;
        }
        if (Modifier.isAbstract(modifier)) {
            return false;
        }
        if (Modifier.isNative(modifier)) {
            return false;
        }
        return true;
    }

    private static List<Method> _getAllMethodsFromObjectClass(Class objClass) {
        if (objClass == null) {
            return null;
        }
        List<Method> tmpResults = new ArrayList<Method>();
        //所有公用（public）方法包括其继承类的公用方法，
        // 当然也包括它所实现接口的方法。
        Method[] tmpMethods1 = objClass.getMethods();
        if (tmpMethods1 != null && tmpMethods1.length > 0) {
            for (Method m : tmpMethods1) {
                tmpResults.add(m);
            }
        }
        //对象表示的类或接口声明的所有方法，包括公共、保护、默认（包）访问和私有方法，
        // 但不包括继承的方法。当然也包括它所实现接口的方法。
        Method[] tmpMethods2 = objClass.getClass().getDeclaredMethods();
        if (tmpMethods2 != null && tmpMethods2.length > 0) {
            for (Method m : tmpMethods2) {
                tmpResults.add(m);
            }
        }
        return tmpResults;
    }

    private static List<Field> _getAllFieldsFromObjectClass(Class objClass) {
        if (objClass == null) {
            return null;
        }
        Field[] fromAllPublicFields = objClass.getFields();//获得某个类的所有的公共（public）的字段，包括父类
        Field[] fromAllFieldsInTheObject = objClass.getDeclaredFields();//获得某个类的所有申明的字段，即包括public、private和proteced，但是不包括父类的申明字段
        if (fromAllPublicFields == null || fromAllPublicFields.length == 0 || fromAllFieldsInTheObject == null || fromAllFieldsInTheObject.length == 0) {
            return null;
        }
        List<Field> tmpResults = new ArrayList<Field>();
        for (Field field : fromAllFieldsInTheObject) {
            if (!Modifier.isPublic(field.getModifiers())) {
                tmpResults.add(field);
            }
        }
        for (Field field : fromAllPublicFields) {
            tmpResults.add(field);
        }
        return tmpResults;
    }
}
